iOS 网络通讯 - HTTP通讯

与服务器进行通讯是每个程序员必备的技能,下面我们将搭建一个服务器,并通过这个服务器了解 Swift 语言是如何与服务器进行通讯的。

搭建J2E开发环境

搭建服务器之前首先确认已经具有以下开发环境:

Eclipse IDE Apache Tomcat JDK
必须 必须 可选
Java 编译器 基于 Java 的 Web 服务器 Java 的运行环境
下载地址 下载地址 下载地址

首先使用 Eclipse IDE 新建一个 Dynamic Web Project:

填写 Project Name,选择 New Runtime:

选择一个 Apache Tomcat,点击 Next:

选择 Browse,找到下载的 Apache Tomcat,并填写 Tomcat Server 的 Name,点击 Finish:

再次点击 Finish,一个 Web 服务器就搭建好了。

GET方式通讯

在 WebContent 中新建一个 JSP File:

填写 File Name,就得到一个 JSP 文件。

JSP简介

JSP 全名为 Java Server Pages,中文名叫 Java 服务器页面,其根本是一个简化的 Servlet 设计,它是由 Sun Microsystems 公司倡导、许多公司参与一起建立的一种动态网页技术标准。

JSP技术有点类似ASP技术,它是在传统的网页 HTML 文件中,插入 Java 程序段也就是 Scriptlet,和 JSP 标记 Tag,从而形成 JSP 文件。

用 JSP 开发的 Web 应用是跨平台的,既能在 Linux 下运行,也能在其他操作系统上运行。

它实现了 Html 语法中的 Java 扩展(以 <%, %>形式)。JSP 与 Servlet 一样,是在服务器端执行的。通常返回给客户端的就是一个 HTML 文本,因此客户端只要有浏览器就能浏览。

以上就是 JSP 的简单介绍,此时我们创建的 JSP 文件内容是这样的:

1
2
3
4
5
6
7
8
9
10
11
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
</body>
</html>

可以看出,它同时包括 Java 和 Html,现在我们将 Html 的部分删掉,将这个文件修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String name = request.getParameter("name");
if (name != null) {
out.print("Hello " + name);
}
else {
out.print("No args");
}
%>>

然后运行这个 Project,当成功启动后,我们就可以用下面的地址访问新建的 Java 服务器页面:

1
http://localhost:8080/your-project-name/your-jspfile-name.jsp

我们写在服务器的代码相信很容易看懂,使用 String 类型的属性 name 获取名为 name 的值,如果这个值不为空,就输出 Hello + name,如果是空的,输出 No args。因为我们还没有给它传值,所以网页上的显示内容是:

1
No args

如果我们将网址改为下面这样:

1
http://localhost:8080/your-project-name/your-jspfile-name.jsp?name=geek

网页的显示内容为:

1
Hello geek

我们将 ?key=value 这种直接写在地址栏的通讯方式称为 get 方式通讯,如果有多个值需要传递到服务器,使用 & 符号:

1
http://localhost:8080/your-project-name/your-jspfile-name.jsp?name=geek&age=20

Swift语言下的GET通讯

下面我们来介绍,如何使用 Swift 语言和服务器进行通讯,首先创建 3 个控件:

1
2
3
4
5
6
7
@IBOutlet weak var inputField: UITextField!
@IBOutlet weak var outTextView: UITextView!
@IBAction func connectBtn(sender: AnyObject) {
}

点击 Button 时,将 TextField 的文本与我们创建的服务器页面合成在一起:

1
2
3
4
5
6
7
8
9
@IBOutlet weak var inputField: UITextField!
@IBOutlet weak var outTextView: UITextView!
@IBAction func connectBtn(sender: AnyObject) {
let str = "http://localhost:8080/MyServer/Hello.jsp?name=\(inputField.text!)"
}

创建一个方法,获取服务器的数据:

1
2
3
4
5
6
7
8
9
10
11
12
func runInWebServer(str: String) {
let session = NSURLSession.sharedSession()
let url = NSURL(string: str)
let request = NSURLRequest(URL: url!)
let task = session.dataTaskWithRequest(request) { (data: NSData?, resp: NSURLResponse?, error: NSError?) in
}
task.resume()
}

将数据呈现在 TextView 上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func runInWebServer(str: String) {
let session = NSURLSession.sharedSession()
let url = NSURL(string: str)
let request = NSURLRequest(URL: url!)
let task = session.dataTaskWithRequest(request) { (data: NSData?, resp: NSURLResponse?, error: NSError?) in
if error != nil {
dispatch_async(dispatch_get_main_queue(), {
self.outTextView.text = "出错了"
})
}
else {
if let d = data {
let html = String(data: d, encoding: NSUTF8StringEncoding)
dispatch_async(dispatch_get_main_queue(), {
self.outTextView.text = html
})
}
}
}
task.resume()
}

在 Action 方法中调用 runInWebServer 方法:

1
2
3
4
5
6
@IBAction func connectBtn(sender: AnyObject) {
let str = "http://localhost:8080/MyServer/Hello.jsp?name=\(inputField.text!)"
runInWebServer(str)
}

此时我们就完成了 Swift 语言下 GET 方式与服务器通讯。

Swift语言下的POST通讯

GET 通讯方式的优势在于可以通过一个 URL 将资源进行定位。 GET 通讯方式对于比较大的数据传输支持并不太好,因为它只能写在网址中,它传输内容的大小取决于服务器取地址栏的长度。

比如说服务器端取地址栏的长度是 500 个字节的话,那么传给服务器的网址长度超过 500 字节后的内容就会丢失。

POST通讯方式不会把参数写在地址栏中,它的优势在于可以传递庞大的数据。

使用POST通讯的流程如下:

  1. 获取服务器页面的 URL:

    1
    let url = NSURL(string: "http://localhost:8080/MyServer/Hello.jsp")
  2. 通过 URL 创建可变的网络请求:

    1
    let mutableRequest = NSMutableURLRequest(URL: url!)
  3. 显式将 HTTP 传输方式指明为 POST:

    1
    mutableRequest.HTTPMethod = "POST"
  4. 创建传递给服务器的数据:

    1
    2
    let data = String("name=\(inputField.text!)").dataUsingEncoding(NSUTF8StringEncoding)
    mutableRequest.HTTPBody = data
  5. 创建通讯,将数据传递给服务器:

    1
    2
    3
    4
    5
    6
    7
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(mutableRequest) { (data: NSData?, resp: NSURLResponse?, error: NSError?) in
    dispatch_sync(dispatch_get_main_queue(), {
    self.outTextView.text = String(data: data!, encoding: NSUTF8StringEncoding)
    })
    }
    task.resume()

需要注意的是 HTTPBody 传输的数据虽然在理论上是无限大,但一般控制在2M以内,根据服务器设置的不同,接收的数据大小也有所浮动。

一般来说 POST方式传输的数据大小一般在100M以内,如果超过100M就超出了HTTP传输的范畴,我们一般会采用其他方式进行传输。

客户端的编码方式需要和服务器一致,否则会出错。

POST通讯的完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func runInWebServerPost() {
let url = NSURL(string: "http://localhost:8080/MyServer/Hello.jsp")
let mutableRequest = NSMutableURLRequest(URL: url!)
mutableRequest.HTTPMethod = "POST"
let data = String("name=\(inputField.text!)").dataUsingEncoding(NSUTF8StringEncoding)
mutableRequest.HTTPBody = data
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(mutableRequest) { (data: NSData?, resp: NSURLResponse?, error: NSError?) in
dispatch_sync(dispatch_get_main_queue(), {
self.outTextView.text = String(data: data!, encoding: NSUTF8StringEncoding)
})
}
task.resume()
}